package com.simpou.tests.persistence.jpa;

import com.simpou.commons.persistence.jpa.helper.JpaExceptionHelper;
import com.simpou.commons.utils.behavior.Randomizable;
import com.simpou.commons.utils.lang.annot.RandomFillableConfig;
import com.simpou.commons.utils.reflection.Casts;
import com.simpou.commons.utils.tests.TestRunner;
import com.simpou.commons.utils.tests.annot.IgnoreValidation;
import com.simpou.commons.utils.tests.annot.TestOrder;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.validation.ConstraintViolationException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@IgnoreValidation
@RunWith(TestRunner.class)
public abstract class JpaEntitiesTester {

    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    protected class Config {

        private String puName;

        private String pkgName;

    }

    protected abstract Config init();
    private static EntityManager em;

    private static List<Class<?>> scanneds;

    private static Config config;

    private final Log logger = LogFactory.getLog(this.getClass());

    @Test
    @TestOrder(1)
    public void setup() {
        config = init();
        EntityManagerFactory emf = JpaTesterHelper.newEMFactory(config.getPuName());
        em = emf.createEntityManager();
        scanneds = JpaTesterHelper.scanEntities(config.getPuName(), config.getPkgName());
    }

    @Test
    @TestOrder(2)
    public void ping() {
        for (Class<?> scanned : scanneds) {
            final String query = "SELECT 'ping' FROM " + scanned.getSimpleName() + " e";
            em.createQuery(query, String.class).getResultList();
        }
    }

    @Test
    @TestOrder(3)
    public void create() throws InstantiationException, IllegalAccessException {
        boolean errorOcurred = false;
        for (final Class<?> scanned : scanneds) {
            if(scanned.isAnnotationPresent(RandomFillableConfig.class)
                    && scanned.getAnnotation(RandomFillableConfig.class).ignore()){
                continue;
            }
            if (Randomizable.class.isAssignableFrom(scanned)) {
                Randomizable randomizable = Casts.simpleCast(scanned.newInstance());
                randomizable.fillRandom();
                EntityTransaction tx = em.getTransaction();
                try {
                    tx.begin();
                    em.persist(randomizable);
                    tx.commit();
                } catch (ConstraintViolationException ex) {
                    errorOcurred = true;
                    logger.error(JpaExceptionHelper.toString(ex));
               } catch (Exception ex) {
                    errorOcurred = true;
                    logger.error("Entity "+scanned, ex);
                }finally{
                    if (tx.isActive()) {
                        tx.rollback();
                    }
                }
            } else {
                logger.warn("Entity " + scanned + " is not a " + Randomizable.class.getName()+". Create tests will skip.");
            }
        }
        if(errorOcurred){
            Assert.fail();
        }
    }
}
